//
//  NetSession.cpp
//  FreeNet
//
//  Created by cc on 12-7-5.
//  Copyright (c) 2012年 cc. All rights reserved.
//

#include "NetSession.h"

namespace FreeNet {

    CEpoll::CEpoll(const int max, const char* ip, const int port, const eNetMode mode)
    {
        remoteEvents = 0 ;
        remoteCount = 0 ;
        ::memset(&localInfo, 0, sizeof(sNetInfo)) ;
        epollFD = -1 ;

        strcpy(localInfo.ip, ip) ;
        localInfo.port = port ;
        localInfo.mode = mode ;

        localInfo.fd = ::socket(AF_INET, SOCK_STREAM, 0) ;
        if (-1 == localInfo.fd)
        {
            showError(err_str[0]) ;
            return ;
        }

        sockaddr_in localAddr ;
        localAddr.sin_family = AF_INET ;
        localAddr.sin_addr.s_addr = inet_addr(localInfo.ip) ;
        localAddr.sin_port = htons(localInfo.port) ;
        
        int flag ;
        if (-1 == setsockopt(localInfo.fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)))
        {
            showError(err_str[1]) ;
            ::close(localInfo.fd) ;
            return ;
        }
        if (-1 == ::bind(localInfo.fd, (struct sockaddr*)&localAddr, sizeof(struct sockaddr)))
        {
            showError(err_str[2]) ;
            ::close(localInfo.fd) ;
            return ;
        }
        if (-1 == ::listen(localInfo.fd, 5))
        {
            showError(err_str[4]) ;
            ::close(localInfo.fd) ;
            return ;
        }
        
        struct rlimit rl ;
        rl.rlim_max = rl.rlim_cur = max ;
        assert(-1 != setrlimit(RLIMIT_NOFILE, &rl)) ;
        epollFD = ::epoll_create(max) ;
        assert(-1 != epollFD) ;

        localEvent.events = EPOLLIN | EPOLLET ;
        localEvent.data.fd = localInfo.fd ;
        int op = fcntl(epollFD, F_GETFD, 0) ;
        assert(-1 != op) ;
        assert(-1 != fcntl(epollFD, F_SETFL, op | O_NONBLOCK)) ;

        if (!Ctl(EPOLL_CTL_ADD, localInfo.fd))
        {
            ::close(localInfo.fd) ;
            return ;
        }
        remoteEvents = new epoll_event[max] ;
        ::printf("create epoll done! \n") ;
    }

    CEpoll::~CEpoll()
    {
        delete [] remoteEvents ;
    }

    int CEpoll::wait(const int timeout)
    {
        int fds = ::epoll_wait(epollFD, remoteEvents, remoteCount, timeout) ;
        if (-1 == fds)
        {
            ::printf("epoll wait error [%d]\n", epollFD) ;
        }
        return fds ;
    }

    void CEpoll::destroy()
    {
    }

    bool CEpoll::Ctl(const int op, const int fd)
    {
        int res = ::epoll_ctl(epollFD, op, fd, &localEvent) ;
        if (-1 == res)
        {
            ::printf("epoll ctl error [%d] [%d]\n", op, fd) ;
            return false ;
        }
        
        switch (op)
        {
            case EPOLL_CTL_ADD: ++remoteCount ; break ;
            case EPOLL_CTL_MOD: break ;
            case EPOLL_CTL_DEL:
            {
                --remoteCount ;
                ::close(localInfo.fd) ;
            } break ;
            default: break ;
        }
        
        return true ;
    }


    // c api.
    int connectServer(const char* ip, const int port, const eNetMode mode, NET_CALLBACK cb)
    {
        int fd = ::socket(AF_INET, SOCK_STREAM, mode) ;
        int flag ;

        sockaddr_in localAddr ;
        localAddr.sin_family = mode ;
        localAddr.sin_addr.s_addr = inet_addr(ip) ;
        localAddr.sin_port = htons(port) ;

        if (-1 == fd)
        {
            showError(err_str[0]) ;
            return -1 ;
        }
        if (-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)))
        {
            showError(err_str[1]) ;
            ::close(fd) ;
            return -1 ;
        }
        if (-1 == ::connect(fd, (struct sockaddr*)&localAddr, sizeof(struct sockaddr)))
        {
            showError(err_str[3]) ;
            ::close(fd) ;
            return -1 ;
        }
        
        if (0 != cb) { cb() ; }
        return fd ;
    }

    int disconnectSession(const int fd, NET_CALLBACK cb)
    {
        int res = ::close(fd) ;
        if (0 != cb) { cb() ; }
        return res ;
    }
    
    size_t sendMessage(const void* data, const int len, const int fd, NET_CALLBACK cb)
    {
        size_t res = ::send(fd, data, len, 0) ;
        if (0 != cb) { cb() ; }
        return res ;
    }

    size_t recvMessage(void* data, size_t len, const int fd, NET_CALLBACK cb)
    {
        size_t res = ::recv(fd, data, len, 0) ;
        if (0 != cb) { cb() ; }
        return res ;
    }

    size_t sendMessageTo(const char* ip, const int port, const void* data, const int len, NET_CALLBACK cb)
    {
        sockaddr_in addr ;
        addr.sin_family = AF_INET ;
        addr.sin_addr.s_addr = inet_addr(ip) ;
        addr.sin_port = htons(port) ;

        size_t res = ::sendto(0, data, len, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr)) ;
        if (0 != cb) { cb() ; }
        return res ;
    }
    
    size_t recvMessageFrom(const char* ip, const int port, void* data, size_t len, NET_CALLBACK cb)
    {
        sockaddr_in addr ;
        addr.sin_family = AF_INET ;
        addr.sin_addr.s_addr = inet_addr(ip) ;
        addr.sin_port = htons(port) ;

        socklen_t flag ;
        size_t res = ::recvfrom(0, data, len, 0, (struct sockaddr*)&addr, &flag) ;
        if (0 != cb) { cb() ; }
        return res ;
    }
}












